package cn.itsource.ymcc.service.impl;

import cn.itsource.ymcc.dto.CourseDocumentDto;
import cn.itsource.ymcc.dto.CourseListQueryDto;
import cn.itsource.ymcc.query.BaseQuery;
import cn.itsource.ymcc.repostory.ICourseRepository;
import cn.itsource.ymcc.service.ICourseService;
import cn.itsource.ymcc.utils.HighlightResultMapper;
import cn.itsource.ymcc.vo.AggResult;
import cn.itsource.ymcc.vo.CourseListVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.queryparser.xml.builders.BooleanQueryBuilder;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.support.ValueType;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ICourseServiceImpl implements ICourseService {

    @Autowired
    private ICourseRepository courseRepository;

    @Autowired
    private ElasticsearchRestTemplate template;

    /**
     * 查询课程列表
     * @param courseListQueryDto
     * @return
     */
    @Override
    public CourseListVO course(CourseListQueryDto courseListQueryDto) {
        // 1. 获取参数
        String chargeName = courseListQueryDto.getChargeName();
        String gradeName = courseListQueryDto.getGradeName();
        Long courseTypeId = courseListQueryDto.getCourseTypeId();
        BigDecimal priceMax = courseListQueryDto.getPriceMax();
        BigDecimal priceMin = courseListQueryDto.getPriceMin();
        String keyword = courseListQueryDto.getKeyword();
        String sortField = courseListQueryDto.getSortField();
        String sortType = courseListQueryDto.getSortType();
        Integer rows = courseListQueryDto.getRows();
        Integer page = courseListQueryDto.getPage();

        List<CourseDocumentDto> documentDtos = new ArrayList<>();

// ===============================构造查询条件 ===========================================================================
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();

        boolquery(chargeName, gradeName, courseTypeId, priceMax, priceMin, keyword, searchQueryBuilder);

// ================================= 查询结果的展示 ===============================================================================

        pageSort(sortField, sortType, rows, page, searchQueryBuilder);


        //高亮查询的条件封装
        searchQueryBuilder.withHighlightBuilder(new HighlightBuilder().field("name")
                .preTags("<b style='color:red'>").postTags("</b>"));

// ================================= 聚合查询的展示 ===============================================================================

        //聚合查询 -- 等级，是否收费
        searchQueryBuilder.addAggregation(new TermsAggregationBuilder("gradeAgg", ValueType.STRING).field("gradeName"));
        searchQueryBuilder.addAggregation(new TermsAggregationBuilder("chargeAgg", ValueType.LONG).field("charge"));

// ===============================执行查询 =======================================================================
       // Page<CourseDocumentDto> search = courseRepository.search(searchQueryBuilder.build());

        AggregatedPage<CourseDocumentDto> search = template.queryForPage(searchQueryBuilder.build(),
                CourseDocumentDto.class, new HighlightResultMapper());

        AggResult aggResult = getAggResult(search);

// ===============================封装查询结果 =======================================================================

        search.forEach(course->{
            documentDtos.add(course);
        });

        CourseListVO courseListVO = new CourseListVO();
        courseListVO.setTotal(search.getTotalElements());
        courseListVO.setRows(documentDtos);
        courseListVO.setAggResult(aggResult);

        return courseListVO;
    }

    private AggResult getAggResult(AggregatedPage<CourseDocumentDto> search) {
        AggResult aggResult = new AggResult();

        Aggregation gradeAggregation = search.getAggregation("gradeAgg");
        Aggregation chargeAggregation = search.getAggregation("chargeAgg");

        List<? extends Terms.Bucket> gradeBuckets = ((ParsedStringTerms) gradeAggregation).getBuckets();
        List<Map> gradeAggregationList = new ArrayList<>();

        for (Terms.Bucket bucket : gradeBuckets) {
            Map map = new ConcurrentHashMap();
            map.put("key",bucket.getKeyAsString());
            map.put("docCount",bucket.getDocCount());
            gradeAggregationList.add(map);
        }

        List<? extends Terms.Bucket>  chargeBuckets = ((ParsedLongTerms) chargeAggregation).getBuckets();
        List<Map> chargeAggregationList  = chargeBuckets.stream().map(bucket -> {
            Map map = new ConcurrentHashMap();
            map.put("key", Long.valueOf(bucket.getKeyAsString()).equals(1L)?"免费":"收费");
            map.put("docCount", bucket.getDocCount());
            return map;
        }).collect(Collectors.toList());

        // Map {
        //  key: "黄金"，
        //  countdoc：4
        // }
        aggResult.setGradeNameTermsAgg(gradeAggregationList);
        aggResult.setChargeNameTermsAgg(chargeAggregationList);
        return aggResult;
    }

    private void pageSort(String sortField, String sortType, Integer rows, Integer page, NativeSearchQueryBuilder searchQueryBuilder) {
        String sortfieldName = "saleCount"; // 销量排序
        SortOrder sortOrder = SortOrder.DESC;

        if(sortField==null){
            sortfieldName = "saleCount";
        }else{
            switch (sortField){
                case "xp" : sortfieldName = "onlineTime"; break; // 新品
                case "pl" : sortfieldName = "commentCount"; break; // 评论
                case "jg" : sortfieldName = "price"; break;// 价格
                case "rq" : sortfieldName = "viewCount"; break; // 人气
                case "xl" : sortfieldName = "saleCount"; break; // 销量
                default: sortfieldName = "saleCount";
            }
        }
        if(sortType==null){
            sortOrder = SortOrder.DESC;
        }else{
            sortOrder = sortType.equals("desc")?SortOrder.DESC:SortOrder.ASC;
        }


        searchQueryBuilder.withSort(new FieldSortBuilder(sortfieldName).order(sortOrder));
        // 分页 从0开始
        searchQueryBuilder.withPageable(PageRequest.of(page -1, rows));
    }

    private void boolquery(String chargeName, String gradeName, Long courseTypeId, BigDecimal priceMax, BigDecimal priceMin, String keyword, NativeSearchQueryBuilder searchQueryBuilder) {
        BoolQueryBuilder boolQuery = new BoolQueryBuilder();

// ===============================构造查询条件 DSL 查询=======================================================================
        // keyword 不为空，才根据课程名进行 全文检索
        if(keyword !=null){
            boolQuery.must(new MatchQueryBuilder("name", keyword));
        }

// ===============================构造查询条件 DSL 过滤 =======================================================================
        // 课程类型
        if(courseTypeId !=null){
            boolQuery.filter(new TermQueryBuilder("courseTypeId", courseTypeId));
        }

        // 价钱的范围
        if(priceMax !=null){
            boolQuery.filter(new RangeQueryBuilder("price").lte(priceMax));
        }

        if(priceMin !=null){
            boolQuery.filter(new RangeQueryBuilder("price").gte(priceMin));
        }

        // 是否收费
        if(chargeName !=null){
            boolQuery.filter(new TermQueryBuilder("charge", chargeName.equals("收费")?2:1));
        }

        // 等级
        if(gradeName !=null){
            boolQuery.filter(new TermQueryBuilder("gradeName", gradeName));
        }

        searchQueryBuilder.withQuery(boolQuery);
    }
}
